using System;
using System.IO;

namespace Pegasus.IO
{
	/// <summary>
	/// Add addition features to the directory class.
	/// </summary>
	public static class DirectoryHelper
	{
		/// <summary>
		/// Copies a directory.
		/// </summary>
		/// <param name="srcDir">The source dir.</param>
		/// <param name="targetDir">The target dir.</param>
		/// <param name="recursive">if set to <c>true</c> will copy sub-folders.</param>
		public static void CopyDirectory( string srcDir, string targetDir, bool recursive )
		{
			DirectoryInfo srcPath = new DirectoryInfo( srcDir );
			DirectoryInfo targetPath = new DirectoryInfo( targetDir );

			CopyDirectory( srcPath, targetPath, recursive );
		}

		/// <summary>
		/// Copies a directory.
		/// </summary>
		/// <param name="srcPath">The source directory info object.</param>
		/// <param name="targetPath">The target directory info object.</param>
		/// <param name="recursive">if set to <c>true</c> will copy sub-folders</param>
		public static void CopyDirectory( DirectoryInfo srcPath, DirectoryInfo targetPath, bool recursive )
		{
			// If the target folder does not exist then create it.
			if( !targetPath.Exists )
			{
				targetPath.Create();
			}

			// Copy files first
			foreach( FileInfo fileInfo in srcPath.GetFiles() )
			{
				fileInfo.CopyTo( Path.Combine( targetPath.FullName, fileInfo.Name ), true );
			}

			// Copy sub-folder 
			if( recursive )
			{
				foreach( DirectoryInfo dirInfo in srcPath.GetDirectories() )
				{
					CopyDirectory( dirInfo, new DirectoryInfo( Path.Combine( targetPath.FullName, dirInfo.Name ) ),
                        recursive );
				}
			}
		}

        /// <summary>
        /// Copies a directory.
        /// </summary>
        /// <param name="srcDir">The source dir</param>
        /// <param name="targetDir">The target dir</param>
        /// <param name="recursive">if set to <c>true</c> will copy sub-folders</param>
        /// <param name="copyOption">The copy option</param>
        public static void CopyDirectory(string srcDir, string targetDir, bool recursive, CopyOption copyOption)
        {
            DirectoryInfo srcPath = new DirectoryInfo(srcDir);
            DirectoryInfo targetPath = new DirectoryInfo(targetDir);

            CopyDirectory(srcPath, targetPath, recursive, copyOption);
        }

		/// <summary>
		/// Copies a directory.
		/// </summary>
		/// <param name="srcPath">The SRC path.</param>
		/// <param name="targetPath">The target path.</param>
		/// <param name="recursive">if set to <c>true</c> will copy sub-folders</param>
		/// <param name="copyOption">The copy option</param>
        public static void CopyDirectory(DirectoryInfo srcPath, DirectoryInfo targetPath, bool recursive,
            CopyOption copyOption)
        {
            // If the target folder does not exist then create it.
            if (!targetPath.Exists)
            {
                targetPath.Create();
            }

            // Copy files first
            foreach (FileInfo fileInfo in srcPath.GetFiles())
            {
                switch (copyOption)
                {
                    case CopyOption.DoNotOverwrite:
                    case CopyOption.OverwriteIfNewer:
                        FileInfo srcFileInfo = fileInfo;
                        FileInfo targetFileInfo = new FileInfo(Path.Combine(targetPath.FullName, srcFileInfo.Name));
                        
                        if (!targetFileInfo.Exists)
                        {
                            srcFileInfo.CopyTo(Path.Combine(targetPath.FullName, srcFileInfo.Name), false);
                        }
                        else if (copyOption == CopyOption.OverwriteIfNewer)
                        {
                            DateTime srcTime = srcFileInfo.LastWriteTimeUtc;
                            DateTime targetTime = targetFileInfo.LastWriteTimeUtc;

                            if (srcTime > targetTime)
                            {
                                srcFileInfo.CopyTo(Path.Combine(targetPath.FullName, srcFileInfo.Name), true);
                            }
                        }

                        break;

                    case CopyOption.OverwriteAll:
                        fileInfo.CopyTo(Path.Combine(targetPath.FullName, fileInfo.Name), true);

                        break;
                }
                
            }

            // Copy sub-folder 
            if (recursive)
            {
                foreach (DirectoryInfo dirInfo in srcPath.GetDirectories())
                {
                    CopyDirectory(dirInfo, new DirectoryInfo(Path.Combine(targetPath.FullName, dirInfo.Name)),
                        recursive, copyOption);
                }
            }
        }

		/// <summary>
		/// Replicate a directory.
		/// </summary>
		/// <param name="srcDir">The source dir.</param>
		/// <param name="targetDir">The target dir.</param>
		/// <param name="recursive">if set to <c>true</c> will replicate sub-folders.</param>
		/// <remarks>
		/// Replicate differes from CopyDirectory in that it only copies files and folders that
		/// are missing or out of date in the target.  If a file/folder in the target is
		/// newer that the one in the source it is not copied.  If there is a file/folder in the 
		/// target but does not exist in the source it will be deleted.
		/// </remarks>
		public static void ReplicateDirectory( string srcDir, string targetDir, bool recursive )
		{
			DirectoryInfo srcPath = new DirectoryInfo( srcDir );
			DirectoryInfo targetPath = new DirectoryInfo( targetDir );

			ReplicateDirectory( srcPath, targetPath, recursive );
		}

		/// <summary>
		/// Replicate a directory.
		/// </summary>
		/// <param name="srcPath">The source directory info object.</param>
		/// <param name="targetPath">The target directory info object.</param>
		/// <param name="recursive">if set to <c>true</c> will copy sub-folders</param>
		/// <remarks>
		/// Replicate differes from CopyDirectory in that it only copies files and folders that
		/// are missing or out of date in the target.  If a file/folder in the target is
		/// newer that the one in the source it is not copied.  If there is a file/folder in the 
		/// target but does not exist in the source it will be deleted.
		/// </remarks>
		public static void ReplicateDirectory( DirectoryInfo srcPath, DirectoryInfo targetPath, bool recursive )
		{
			// Make sure that the target directory exist
			if( !targetPath.Exists )
			{
				targetPath.Create();
			}

			// Copy new/changed files from source to target first
			foreach( FileInfo srcFileInfo in srcPath.GetFiles() )
			{
				string srcRelitivePath = PathHelper.GetSubPath( srcPath.FullName, srcFileInfo.FullName );
				FileInfo targetFileInfo = new FileInfo( Path.Combine( targetPath.FullName, srcRelitivePath ) );

				bool needToCopy = false;
				if( targetFileInfo.Exists )
				{
					long srcSize = srcFileInfo.Length;
					DateTime srcTime = srcFileInfo.LastWriteTimeUtc;

					long targetSize = targetFileInfo.Length;
					DateTime targetTime = targetFileInfo.LastWriteTimeUtc;

					// If the files sizes are different or the source file is newer than the target then copy.
					if( srcSize != targetSize || srcTime > targetTime )
					{
						needToCopy = true;
					}
				}
				else
				{
					needToCopy = true;
				}

				// If we need to copy then fire a copy command
				if( needToCopy )
				{
					srcFileInfo.CopyTo( targetFileInfo.FullName );
				}
			}

			// Delete files that are in the target directory but not in the source
			foreach( FileInfo targetFileInfo in targetPath.GetFiles() )
			{
				string targetRelitivePath = PathHelper.GetSubPath( targetPath.FullName, targetFileInfo.FullName );
				string sourceFilename = Path.Combine( srcPath.FullName, targetRelitivePath );
				if( !File.Exists( sourceFilename ) )
				{
					// If the file does not exist in the source then delete out of the target
					File.Delete( sourceFilename );
				}
			}

			// Copy sub-directories 
			if( recursive )
			{
				foreach( DirectoryInfo dirInfo in srcPath.GetDirectories() )
				{
					ReplicateDirectory( dirInfo, new DirectoryInfo( Path.Combine( targetPath.FullName, dirInfo.Name ) ),
                        recursive );
				}
			}

			// Delete any sub-directories that in the target dir that is not in the source.
			foreach( DirectoryInfo targetDirInfo in targetPath.GetDirectories() )
			{
				string targetRelitivePath = PathHelper.GetSubPath( targetPath.FullName, targetDirInfo.FullName );
				string sourceDir = Path.Combine( srcPath.FullName, targetRelitivePath );
				if( !Directory.Exists( sourceDir ) )
				{
					// If the directory does not exist in the source then delete out of the target
					Directory.Delete( sourceDir, true );
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
        public enum CopyOption
        {

			/// <summary>
			/// 
			/// </summary>
			DoNotOverwrite = 0,

			/// <summary>
			/// 
			/// </summary>
			OverwriteIfNewer = 1,

			/// <summary>
			/// 
			/// </summary>
			OverwriteAll = 2
        }
	}
}
